using System;
using System.Linq;
using System.Reactive.Concurrency;
using System.Reactive.Linq;
using NAudio.Wave;
using CompositionalAudio.Infrastructure;

namespace CompositionalAudio.IO
{

    public class WaveInRecorder : IStereoSource
	{
        public IObservable<double> ChannelRight { get; private set; }
        public IObservable<double> ChannelLeft { get; private set; }

        WaveIn waveInStream;

		public static int DeviceCount
		{
            get { return WaveOut.DeviceCount; }
		}

		public WaveInRecorder(int device, int SampleRate, int SampleSize, int NoChannnels)
		{
            waveInStream = new WaveIn();
            waveInStream.WaveFormat = new NAudio.Wave.WaveFormat(SampleRate, SampleSize, NoChannnels);

            Func<byte[], int, double> ExtractSample = StaticTools.GetBitToDoubleFunction(SampleSize);

            int noBytesPerChan = (SampleSize / 8);
            int noBytesTotalPerSamp = noBytesPerChan * NoChannnels;
            var back = Observable.FromEventPattern<WaveInEventArgs>(
                                        h => waveInStream.DataAvailable += h,
                                        h => waveInStream.DataAvailable -= h)
                                        .ObserveOn(TaskPoolScheduler.Default)
                                        .Select(ev =>ev.EventArgs.Buffer)           //Get byte[]
                                        .SelectMany(by=>by)                         //stream of bytes
                                        .Buffer(noBytesTotalPerSamp)                //only bytes per sample
                                        .Select(x=>x.ToArray())                     //list to []
                                        .Select(data=>                              //split byte[] to double,double
                                        {
                                            return new multichannelsample()
                                            {
                                                L = ExtractSample(data, 0),
                                                R = ExtractSample(data, noBytesPerChan),  
                                            };
                                        });


            ChannelRight = back.Select(x => x.R).ObserveOn(new EventLoopScheduler()).Publish().RefCount();  // only R
            ChannelLeft = back.Select(x => x.L).ObserveOn(new EventLoopScheduler()).Publish().RefCount();   // only L

            waveInStream.StartRecording();
		}

        private class multichannelsample
        {
            public double L;
            public double R;
        }

		public void Dispose()
		{
            if (waveInStream!=null)
                waveInStream.Dispose();
		}
	}
}
